// Learn TypeScript:
//  - https://docs.cocos.com/creator/2.4/manual/en/scripting/typescript.html
// Learn Attribute:
//  - https://docs.cocos.com/creator/2.4/manual/en/scripting/reference/attributes.html
// Learn life-cycle callbacks:
//  - https://docs.cocos.com/creator/2.4/manual/en/scripting/life-cycle-callbacks.html

const { ccclass, property } = cc._decorator;

/**
 * 摇杆输入数据接口
 */
export interface JoystickInputData {
    /** X轴方向值，范围 -1 到 1 */
    x: number;
    /** Y轴方向值，范围 -1 到 1 */
    y: number;
    /** 摇杆力度，范围 0 到 1 */
    force: number;
    /** 摇杆角度（弧度） */
    angle: number;
    /** 是否正在被操作 */
    isActive: boolean;
}

/**
 * 虚拟摇杆控制器组件
 * 
 * 功能特性：
 * - 背景圆形区域：196.6 × 196.6 像素
 * - 内部控制小球：75.2 × 75.2 像素
 * - 标准化坐标系统：X轴 -1 到 +1，Y轴 -1 到 +1
 * - 支持触摸和鼠标交互
 */
@ccclass
export default class VirtualJoystickController extends cc.Component {

    @property(cc.Node)
    joystickBackground: cc.Node = null; // 摇杆背景圆形区域

    @property(cc.Node)
    joystickKnob: cc.Node = null; // 摇杆控制小球

    @property({
        displayName: "摇杆回调间隔(秒)",
        tooltip: "摇杆数据回调的时间间隔，0表示每帧都回调"
    })
    callbackInterval: number = 0.016; // 约60FPS

    @property({
        displayName: "点击生成摇杆",
        tooltip: "启用后，点击屏幕任意位置生成摇杆，松开后回到默认位置"
    })
    enableClickToGenerate: boolean = true;

    @property({
        displayName: "检测区域",
        tooltip: "用于检测点击的区域节点，通常是整个屏幕或游戏区域"
    })
    touchDetectionArea: cc.Node = null;

    // 摇杆规格常量
    private readonly BACKGROUND_RADIUS = 196.6 / 2; // 背景半径
    private readonly KNOB_SIZE = 75.2; // 小球尺寸
    private readonly MAX_DISTANCE = this.BACKGROUND_RADIUS - this.KNOB_SIZE / 2; // 小球最大移动距离

    // 状态变量
    private _isActive: boolean = false;
    // 默认初始化，防止外部在本组件 onLoad 之前调用 API 时出现空引用
    private _currentInput: JoystickInputData = {
        x: 0,
        y: 0,
        force: 0,
        angle: 0,
        isActive: false,
    };
    private _lastCallbackTime: number = 0;
    private _defaultPosition: cc.Vec3; // 摇杆的默认位置
    private _isAtDefaultPosition: boolean = true; // 是否在默认位置
    private _interactable: boolean = true; // 是否可交互
    // 当前占用摇杆的触点ID（多指时只接受第一个触点），null 表示无人占用
    private _activeTouchId: number | null = null;

    // 回调函数
    private _onJoystickMove: (inputData: JoystickInputData) => void = null;
    private _onJoystickStart: (inputData: JoystickInputData) => void = null;
    private _onJoystickEnd: (inputData: JoystickInputData) => void = null;

    protected onLoad(): void {
        this.initializeJoystick();
       
    }

    protected start(): void {
        this.setupTouchEvents();
    }

    /**
     * 初始化摇杆
     */
    private initializeJoystick(): void {
        // 初始化输入数据
        this._currentInput = {
            x: 0,
            y: 0,
            force: 0,
            angle: 0,
            isActive: false
        };

        // 保存摇杆的默认位置
        if (this.joystickBackground) {
            this._defaultPosition = this.joystickBackground.position.clone();
            this.joystickBackground.width = 196.6;
            this.joystickBackground.height = 196.6;
        }

        if (this.joystickKnob) {
            this.joystickKnob.width = this.KNOB_SIZE;
            this.joystickKnob.height = this.KNOB_SIZE;
            // 确保小球在中心位置
            this.joystickKnob.position = cc.v3(0, 0, 0);
        }

        this._isAtDefaultPosition = true;
    }

    /**
     * 设置触摸事件
     */
    private setupTouchEvents(): void {
        if (this.enableClickToGenerate) {
            this.setupClickToGenerateMode();
        } else {
            this.setupFixedPositionMode();
        }
    }

    /**
     */
    private setupClickToGenerateMode(): void {
        const detectionNode = this.touchDetectionArea || this.node.parent || cc.director.getScene();

        if (!detectionNode) {
            cc.warn("VirtualJoystickController: 无法找到触摸检测区域");
            return;
        }

        // 在检测区域上监听触摸事件
        detectionNode.on(cc.Node.EventType.TOUCH_START, this.onScreenTouchStart, this);
        detectionNode.on(cc.Node.EventType.TOUCH_MOVE, this.onTouchMove, this);
        detectionNode.on(cc.Node.EventType.TOUCH_END, this.onTouchEnd, this);
        detectionNode.on(cc.Node.EventType.TOUCH_CANCEL, this.onTouchEnd, this);
    }

    /**
     * 设置固定位置模式的触摸事件
     */
    private setupFixedPositionMode(): void {
        if (!this.joystickBackground) {
            cc.warn("VirtualJoystickController: joystickBackground 未设置");
            return;
        }

        // 在摇杆背景上监听触摸事件
        this.joystickBackground.on(cc.Node.EventType.TOUCH_START, this.onTouchStart, this);
        this.joystickBackground.on(cc.Node.EventType.TOUCH_MOVE, this.onTouchMove, this);
        this.joystickBackground.on(cc.Node.EventType.TOUCH_END, this.onTouchEnd, this);
        this.joystickBackground.on(cc.Node.EventType.TOUCH_CANCEL, this.onTouchEnd, this);
    }

    /**
     * 屏幕触摸开始事件（点击生成模式）
     */
    private onScreenTouchStart(event: cc.Event.EventTouch): void {
        if (!this._interactable) return;
        const id = event.getID();
        // 若已有其他手指占用，则忽略本次触摸
        if (this._activeTouchId !== null && this._activeTouchId !== id) {
            return;
        }
        // 认领触点
        if (this._activeTouchId === null) this._activeTouchId = id;
        // 将摇杆移动到点击位置
        const touchLocation = event.getLocation();
        const worldPos = this.node.parent.convertToNodeSpaceAR(touchLocation);

        // 移动摇杆到点击位置
        this.joystickBackground.position = cc.v3(worldPos.x, worldPos.y, 0);
        this._isAtDefaultPosition = false;

        // 激活摇杆
        this._isActive = true;
        this._currentInput.isActive = true;

        // 重置小球位置到摇杆中心
        this.joystickKnob.position = cc.v3(0, 0, 0);

        if (this._onJoystickStart) {
            this._onJoystickStart(this.getCurrentInputData());
        }
    }

    /**
     * 触摸开始事件（固定位置模式）
     */
    private onTouchStart(event: cc.Event.EventTouch): void {
        if (!this._interactable) return;
        const id = event.getID();
        // 若已有其他手指占用，则忽略
        if (this._activeTouchId !== null && this._activeTouchId !== id) {
            return;
        }
        // 认领触点
        if (this._activeTouchId === null) this._activeTouchId = id;
        this._isActive = true;
        this._currentInput.isActive = true;

        const touchPos = this.getTouchPosition(event);
        this.updateJoystickPosition(touchPos);

        if (this._onJoystickStart) {
            this._onJoystickStart(this.getCurrentInputData());
        }
    }

    /**
     * 触摸移动事件
     */
    private onTouchMove(event: cc.Event.EventTouch): void {
        if (!this._isActive || !this._interactable) return;
        // 仅处理占用该摇杆的触点
        if (this._activeTouchId !== null && event.getID() !== this._activeTouchId) {
            return;
        }

        const touchPos = this.getTouchPosition(event);
        this.updateJoystickPosition(touchPos);
    }

    /**
     * 触摸结束事件
     */
    private onTouchEnd(_event: cc.Event.EventTouch): void {
        if (!this._interactable && !this._isActive) return;
        // 仅在结束的是占用触点时才真正结束
        if (this._activeTouchId !== null && _event.getID() !== this._activeTouchId) {
            return;
        }
        this._isActive = false;
        this._currentInput.isActive = false;

        // 重置摇杆位置
        this.resetJoystickPosition();

        // 如果是点击生成模式，将摇杆移回默认位置
        if (this.enableClickToGenerate && !this._isAtDefaultPosition) {
            this.moveJoystickToDefaultPosition();
        }

        if (this._onJoystickEnd) {
            this._onJoystickEnd(this.getCurrentInputData());
        }
        // 释放占用
        this._activeTouchId = null;
    }

    /**
     * 获取触摸位置（相对于摇杆背景）
     */
    private getTouchPosition(event: cc.Event.EventTouch): cc.Vec2 {
        const touchLocation = event.getLocation();
        return this.joystickBackground.convertToNodeSpaceAR(touchLocation);
    }

    /**
     * 更新摇杆位置和输入数据
     */
    private updateJoystickPosition(localPos: cc.Vec2): void {
        // 计算距离中心的偏移
        const distance = localPos.mag();
        
        // 限制在最大距离内
        const clampedDistance = Math.min(distance, this.MAX_DISTANCE);
        
        // 计算标准化方向
        let normalizedX = 0;
        let normalizedY = 0;
        let angle = 0;
        
        if (distance > 0) {
            const direction = localPos.normalize();
            normalizedX = direction.x;
            normalizedY = direction.y;
            angle = Math.atan2(direction.y, direction.x);
            
            // 更新小球位置
            const clampedPos = direction.mul(clampedDistance);
            this.joystickKnob.position = cc.v3(clampedPos.x, clampedPos.y, 0);
        }

        // 更新输入数据
        this._currentInput.x = normalizedX * (clampedDistance / this.MAX_DISTANCE);
        this._currentInput.y = normalizedY * (clampedDistance / this.MAX_DISTANCE);
        this._currentInput.force = clampedDistance / this.MAX_DISTANCE;
        this._currentInput.angle = angle;

        
        // 触发移动回调
        this.triggerMoveCallback();
    }

    /**
     * 重置摇杆位置（只重置小球位置）
     */
    private resetJoystickPosition(): void {
        if (this.joystickKnob) {
            this.joystickKnob.position = cc.v3(0, 0, 0);
        }

        // 重置输入数据
        this._currentInput.x = 0;
        this._currentInput.y = 0;
        this._currentInput.force = 0;
        this._currentInput.angle = 0;
    }

    /**
     * 将摇杆移动到默认位置
     */
    private moveJoystickToDefaultPosition(): void {
        if (this.joystickBackground && this._defaultPosition) {
            this.joystickBackground.position = this._defaultPosition.clone();
            this._isAtDefaultPosition = true;
        }
    }

    /**
     * 触发移动回调
     */
    private triggerMoveCallback(): void {
        if (!this._onJoystickMove) return;

        const currentTime = Date.now() / 1000;
        if (this.callbackInterval <= 0 || currentTime - this._lastCallbackTime >= this.callbackInterval) {
            this._onJoystickMove(this.getCurrentInputData());
            this._lastCallbackTime = currentTime;
        }
    }

    /**
     * 获取当前输入数据的副本
     */
    private getCurrentInputData(): JoystickInputData {
        return {
            x: this._currentInput.x,
            y: this._currentInput.y,
            force: this._currentInput.force,
            angle: this._currentInput.angle,
            isActive: this._currentInput.isActive
        };
    }

    // ========== 公共API ==========

    /**
     * 设置摇杆移动回调
     */
    public setOnJoystickMove(callback: (inputData: JoystickInputData) => void): void {
        this._onJoystickMove = callback;
    }

    /**
     * 设置摇杆开始回调
     */
    public setOnJoystickStart(callback: (inputData: JoystickInputData) => void): void {
        this._onJoystickStart = callback;
    }

    /**
     * 设置摇杆结束回调
     */
    public setOnJoystickEnd(callback: (inputData: JoystickInputData) => void): void {
        this._onJoystickEnd = callback;
    }

    /**
     * 获取当前摇杆输入数据
     */
    public getInputData(): JoystickInputData {
        return this.getCurrentInputData();
    }

    /**
     * 获取移动方向向量（已标准化）
     */
    public getDirection(): cc.Vec2 {
        return new cc.Vec2(this._currentInput.x, this._currentInput.y);
    }

    /**
     * 获取移动速度（考虑力度）
     */
    public getVelocity(maxSpeed: number = 1): cc.Vec2 {
        const direction = this.getDirection();
        return direction.mul(this._currentInput.force * maxSpeed);
    }

    /**
     * 是否正在被操作
     */
    public isActive(): boolean {
        return this._isActive;
    }

    /**
     * 手动将摇杆移动到默认位置
     */
    public moveToDefaultPosition(): void {
        this.moveJoystickToDefaultPosition();
    }

    /**
     * 设置是否可交互
     */
    public setInteractable(enabled: boolean): void {
        this._interactable = enabled;
        if (!enabled) {
            // 禁用时重置输入与位置
            this._isActive = false;
            // 惰性初始化，防止在 onLoad 之前被外部调用导致 _currentInput 为空
            if (!this._currentInput) {
                this._currentInput = { x: 0, y: 0, force: 0, angle: 0, isActive: false };
            }
            this._currentInput.isActive = false;
            // 释放占用，避免残留触点控制
            this._activeTouchId = null;
            this.resetJoystickPosition();
            if (this.enableClickToGenerate && !this._isAtDefaultPosition) {
                this.moveJoystickToDefaultPosition();
            }
            // 视觉上隐藏摇杆，避免用户误以为还能拖动
            if (this.joystickBackground) this.joystickBackground.active = false;
            if (this.joystickKnob) this.joystickKnob.active = false;
        } else {
            // 恢复可交互时，显示摇杆
            if (this.joystickBackground) this.joystickBackground.active = true;
            if (this.joystickKnob) this.joystickKnob.active = true;
        }
    }

    /**
     * 当前是否可交互
     */
    public isInteractable(): boolean {
        return this._interactable;
    }

    /**
     * 设置摇杆的默认位置
     */
    public setDefaultPosition(position: cc.Vec3): void {
        this._defaultPosition = position.clone();
        if (this._isAtDefaultPosition) {
            this.joystickBackground.position = this._defaultPosition.clone();
        }
    }

    /**
     * 获取摇杆的默认位置
     */
    public getDefaultPosition(): cc.Vec3 {
        return this._defaultPosition ? this._defaultPosition.clone() : cc.v3(0, 0, 0);
    }

    /**
     * 摇杆是否在默认位置
     */
    public isAtDefaultPosition(): boolean {
        return this._isAtDefaultPosition;
    }

    protected onDestroy(): void {
        // 清理事件监听
        if (this.enableClickToGenerate) {
            const detectionNode = this.touchDetectionArea || this.node.parent || cc.director.getScene();
            if (detectionNode) {
                detectionNode.off(cc.Node.EventType.TOUCH_START, this.onScreenTouchStart, this);
                detectionNode.off(cc.Node.EventType.TOUCH_MOVE, this.onTouchMove, this);
                detectionNode.off(cc.Node.EventType.TOUCH_END, this.onTouchEnd, this);
                detectionNode.off(cc.Node.EventType.TOUCH_CANCEL, this.onTouchEnd, this);
            }
        } else {
            if (this.joystickBackground) {
                this.joystickBackground.off(cc.Node.EventType.TOUCH_START, this.onTouchStart, this);
                this.joystickBackground.off(cc.Node.EventType.TOUCH_MOVE, this.onTouchMove, this);
                this.joystickBackground.off(cc.Node.EventType.TOUCH_END, this.onTouchEnd, this);
                this.joystickBackground.off(cc.Node.EventType.TOUCH_CANCEL, this.onTouchEnd, this);
            }
        }
    }
}
